
#include "linux_def.h"
#include "core.h"
#include "event.h"

int epoll_init(cycle_t *cycle, int timer);
void epoll_done(cycle_t *cycle);
int epoll_add_event(event_t *ev, int event, int flags);
int epoll_del_event(event_t *ev, int event, int flags);
int epoll_add_connection(connection_t *c, int flags);
int epoll_del_connection(connection_t *c, int flags);
int epoll_process_events(cycle_t *cycle, int timer, int flags);

static int	ep = -1;
static struct epoll_event *event_list;
static int	nevents;

int epoll_init(cycle_t *cycle, int timer)
{
	if (ep == -1){
		ep = epoll_create(511);
		if (ep == -1){
			fprintf(stderr, "epoll_create( ) failed.\n");
			return -1;
		}
	}

	event_list = (struct epoll_event*)malloc(sizeof(struct epoll_event) * 511);	//cycle->epoll_num
	if (event_list == NULL)
		return -1;

	nevents = 511;

	event_flags = USE_CLEAR_EVENT | USE_GREEDY_EVENT | USE_EPOLL_EVENT;

//	if (verbose)
//		printf("%s, %d, %s\n", __FILE__, __LINE__,  __func__);

	return 0;
}

void epoll_done(cycle_t *cycle)
{
	close(ep);

	ep = -1;

	free(event_list);
	event_list = NULL;
	nevents = 0;

//	if (verbose)
//		printf("%s, %d, %s\n", __FILE__, __LINE__,  __func__);
}

int epoll_add_event(event_t *ev, int event, int flags)
{
	int	op;
	int	events, prev;
	event_t		*e;
	connection_t	*c;
	struct epoll_event ee;

	c = ev->data;

	events = event;

	if (event == READ_EVENT){
		e = c->write;
		prev = EPOLLOUT;
	}else{
		e = c->read;
		prev = EPOLLIN;
	}

	if (e->active){
		op = EPOLL_CTL_MOD;
		events |= prev;
	}else{
		op = EPOLL_CTL_ADD;
	}

	ee.events = events | (uint32_t)flags;
	ee.data.ptr = (void *)((uintptr_t)c | ev->instance);

	if (epoll_ctl(ep, op, c->fd, &ee) == -1){
		fprintf(stderr, "epoll_clt( add event ) %d . errno = %d, failed.\n", __LINE__, errno);
		return -1;
	}

	ev->active = 1;

//	if (verbose)
//		printf("%s, %d, %s\n", __FILE__, __LINE__,  __func__);

	return 0;
}

int epoll_del_event(event_t *ev, int event, int flags)
{
	int	op;
	int	prev;
	event_t		*e;
	connection_t	*c;
	struct epoll_event	ee;
	
	if (flags & CLOSE_EVENT){
		ev->active = 0;
		return 0;
	}

	c = ev->data;

	if (event == READ_EVENT){
		e = c->write;
		prev = EPOLLOUT;
	}else{
		e = c->read;
		prev = EPOLLIN;
	}

	if (e->active){
		op = EPOLL_CTL_MOD;
		ee.events = prev | (uint32_t)flags;
		ee.data.ptr = (void*)((uintptr_t)c | ev->instance);
	}else{
		op = EPOLL_CTL_DEL;
		ee.events = 0;
		ee.data.ptr = NULL;
	}

	if (epoll_ctl(ep, op, c->fd, &ee) == -1){
		fprintf(stderr, "epoll_ctl( del event ) failed.\n");
		return -1;
	}

	ev->active = 0;
	
//	if (verbose)
//		printf("%s, %d, %s\n", __FILE__, __LINE__,  __func__);

	return 0;
}

int epoll_process_events(cycle_t *cycle, int timer, int flags)
{
	int	err;
	int	events;
	uint32_t	revents;
	int	instance, i;
	
	event_t	*rev, *wev, **queue;
	connection_t	*c;

	events	= epoll_wait(ep, event_list, nevents, timer);
	
	if (events == -1){
		err = errno;
	}else{
		err = 0;	
	}

	if (flags & UPDATE_TIME){
		time_update(0, 0);
	}

	if (err){
		if (errno == EINTR){
			if (event_timer_alarm){
				event_timer_alarm = 0;
				return 0;
			}
		}
		return E_FAIL;
	}

	if (events == 0){
		if (timer != -1)
			return 0;

		return E_FAIL;
	}

	for (i=0; i<events; i++){
		
		c = event_list[i].data.ptr;
		instance = (uintptr_t)c & 1;
	
		c = (connection_t*)((uintptr_t)c & (uintptr_t)~1);
		
		rev = c->read;
		if (c->fd == -1 || rev->instance != instance)
			continue;

		revents = event_list[i].events;

		if (revents & (EPOLLERR | EPOLLHUP)){
			fprintf(stderr, "epoll_wait().\n");
		}

		if ((revents & (EPOLLERR | EPOLLHUP)) && (revents & (EPOLLIN | EPOLLOUT)) == 0){
			revents |= EPOLLIN | EPOLLOUT;
		}

		if ((revents & EPOLLIN) && rev->active){
			rev->ready	= 1;

			if (flags & POST_EVENTS){
				queue = (event_t**)&posted_events;
				locked_post_event(rev, queue);
			}else{
				fprintf(stderr, "=== Read begin === \n");
				rev->handler(rev);
				fprintf(stderr, "=== Read over === \n");
			}
		}
		
		wev = c->write;
		if ((revents & EPOLLOUT) && wev->active){
			wev->ready	= 1;

			if (flags & POST_EVENTS){
				locked_post_event(wev, &posted_events);
			}else{
				fprintf(stderr, "=== Write begin === \n");
				wev->handler(wev);
				fprintf(stderr, "=== Write over === \n");
			}
		}	
	}

	return 0;
}


int epoll_add_connection(connection_t *c, int flags)
{
	struct epoll_event ee;
	
	ee.events = READ_EVENT | WRITE_EVENT | CLEAR_EVENT;
	ee.data.ptr = (void*)((uintptr_t)c | c->read->instance);

	if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1){
		fprintf(stderr, "epoll_add_connection( ) failed.\n");
		return -1;
	}

	c->read->active = 1;
	c->write->active = 1;

	if (verbose)
		printf("%s, %d, %s\n", __FILE__, __LINE__, __func__);

	return 0;
}

int epoll_del_connection(connection_t *c, int flags)
{
	int	op;
	struct epoll_event ee;

	if (flags & CLOSE_EVENT){
		c->read->active = 0;
		c->write->active = 0;
		return 0;
	}

	op = EPOLL_CTL_DEL;
	ee.events = 0;
	ee.data.ptr = NULL;

	if (epoll_ctl(ep, op, c->fd, &ee) == -1){
		fprintf(stderr, "epoll_del_event() failed.\n");
		return -1;	
	}

	c->read->active = 0;
	c->write->active = 0;	

	if (verbose)
		printf("%s, %d, %s\n", __FILE__, __LINE__,  __func__);

	return 0;
}
